/*
 *  FILE: open.c
 *  AUTH: mcc | jal
 *  DESC:
 *  DATE: Mon Apr  6 19:27:49 1998
 */

#include "globals.h"
#include "errno.h"
#include "fs/fcntl.h"
#include "util/string.h"
#include "util/printf.h"
#include "fs/vfs.h"
#include "fs/vnode.h"
#include "fs/file.h"
#include "fs/vfs_syscall.h"
#include "fs/open.h"
#include "fs/stat.h"
#include "util/debug.h"

/* find empty index in p->p_files[] */
int
get_empty_fd(proc_t *p)
{
        int fd;

        for (fd = 0; fd < NFILES; fd++) {
                if (!p->p_files[fd])
                        return fd;
        }

        dbg(DBG_ERROR | DBG_VFS, "ERROR: get_empty_fd: out of file descriptors "
            "for pid %d\n", curproc->p_pid);
        return -EMFILE;
}

/*
 * There a number of steps to opening a file:
 *      1. Get the next empty file descriptor.
 *      2. Call fget to get a fresh file_t.
 *      3. Save the file_t in curproc's file descriptor table.
 *      4. Set file_t->f_mode to OR of FMODE_(READ|WRITE|APPEND) based on
 *         oflags, which can be O_RDONLY, O_WRONLY or O_RDWR, possibly OR'd with
 *         O_APPEND.
 *      5. Use open_namev() to get the vnode for the file_t.
 *      6. Fill in the fields of the file_t.
 *      7. Return new fd.
 *
 * If anything goes wrong at any point (specifically if the call to open_namev
 * fails), be sure to remove the fd from curproc, fput the file_t and return an
 * error.
 *
 * Error cases you must handle for this function at the VFS level:
 *      o EINVAL
 *        oflags is not valid.
 *      o EMFILE
 *        The process already has the maximum number of files open.
 *      o ENOMEM
 *        Insufficient kernel memory was available.
 *      o ENAMETOOLONG
 *        A component of filename was too long.
 *      o ENOENT
 *        O_CREAT is not set and the named file does not exist.  Or, a
 *        directory component in pathname does not exist.
 *      o EISDIR
 *        pathname refers to a directory and the access requested involved
 *        writing (that is, O_WRONLY or O_RDWR is set).
 *      o ENXIO
 *        pathname refers to a device special file and no corresponding device
 *        exists.
 */

int getAccessMode(int oflags)
{

	int accessBits;
	accessBits=oflags&3;

	return accessBits==3?-EINVAL:accessBits;
}

int getAppendMode(int oflags)
{
	return oflags&O_APPEND?1:0;
	}

int getCreateMode(int oflags)
{
	return oflags&O_CREAT?1:0;
	}

void openFileErrorHandler(file_t* errorFile,int errorFileDecriptor, vnode_t *errorNode, int errorType)
{

	if(errorFileDecriptor!=NULL)
	{
		curproc->p_files[errorFileDecriptor] = NULL;
	}
	if(errorFile!=NULL)
	{
		fput(errorFile);
	}
	if(errorNode!=NULL)
	{
		vput(errorNode);
	}

	switch(errorType)
	{
		case -EINVAL:
		dbg(DBG_VFS,"do_open exits errorly with EINVAL");
		break;
		case -EMFILE:
		dbg(DBG_VFS,"do_open exits errorly with EMFILE");
		break;
		case -ENOMEM:
		dbg(DBG_VFS,"do_open exits errorly with ENOMEM");
		break;
		case -ENAMETOOLONG:
		dbg(DBG_VFS,"do_open exits errorly with ENAMETOOLONG");
		break;
		case -ENOENT:
		dbg(DBG_VFS,"do_open exits errorly with ENOENT");
		break;
		case -EISDIR:
		dbg(DBG_VFS,"do_open exits errorly with EISDIR");
		break;
		case -ENXIO:
		dbg(DBG_VFS,"do_open exits errorly with ENXIO");
		break;
	}
}
int
do_open(const char *filename, int oflags)
{
	dbg(DBG_VFS,"enter do_open, try to open %s\n",filename);
		int newFileDescriptor;
		newFileDescriptor=get_empty_fd(curproc);
		if(newFileDescriptor<0)
			return newFileDescriptor;

		file_t* newFile;
		newFile=fget(-1);


	    curproc->p_files[newFileDescriptor] = newFile;

		switch(getAccessMode(oflags))
		{
			case O_WRONLY:
				newFile->f_mode = FMODE_WRITE;
				break;
			case O_RDONLY:
				newFile->f_mode = FMODE_READ;
				break;
			case O_RDWR:
				newFile->f_mode = FMODE_READ | FMODE_WRITE;
				break;
			default:
				openFileErrorHandler(newFile,newFileDescriptor,NULL,-EINVAL);
				return -EINVAL;
		}
		if(getAppendMode(oflags))
		{
			newFile->f_mode=newFile->f_mode | FMODE_APPEND;
		}


		int openCode,ifCreate;
		ifCreate=getCreateMode(oflags);
		vnode_t *retNode;

		openCode = open_namev(filename, ifCreate, &retNode, NULL);
		if(openCode<0)
		{
			openFileErrorHandler(newFile,newFileDescriptor,NULL,openCode);
	        return openCode;
		}

		if(S_ISCHR(retNode->vn_mode))
		{
			retNode->vn_cdev=bytedev_lookup(retNode->vn_devid);
			if(retNode->vn_cdev==NULL)
			{
				openFileErrorHandler(newFile,newFileDescriptor,retNode,-ENXIO);
				return -ENXIO;
			}
		}
		if(S_ISBLK(retNode->vn_mode))
		{
			retNode->vn_bdev=blockdev_lookup(retNode->vn_devid);
			if(retNode->vn_cdev==NULL)
			{
				openFileErrorHandler(newFile,newFileDescriptor,retNode,-ENXIO);
				return -ENXIO;
			}
		}

	    if (S_ISDIR(retNode->vn_mode)&&(oflags!=O_RDONLY))
	    {
	    	openFileErrorHandler(newFile,newFileDescriptor,retNode,-EISDIR);
		return -EISDIR;
	    }

	    newFile->f_pos = 0;
	    newFile->f_vnode = retNode;
	    dbg(DBG_VFS, "open file=%s, oflags=%d, fmode=%d, fd=%d\n", filename, oflags,newFile->f_mode,newFileDescriptor);
	    return newFileDescriptor;

}
